﻿#include "NodeMap.h"

static const int _320 = 320, _240 = 240, _16 = 16, _12 = 12;

static void forScrew(int x0, int y0, int w, int h, const function<bool(int, int)>& cb, int radiusx = 0, int radiusy = 0)
{
	static const int ds[][2] = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };

	int x1 = 0;
	int y1 = 0;
	int x2 = w;
	int y2 = h;
	if (radiusx > 0 && radiusy > 0)
	{
		x1 = std::max(0, x0 - radiusx);
		y1 = std::max(0, y0 - radiusy);
		x2 = std::min(w, x0 + radiusx);
		y2 = std::min(h, y0 + radiusy);
	}
	bool isL = false;
	bool isR = false;
	bool isU = false;
	bool isD = false;
	for (int n = 0;; ++n)
	{
		for (int i = 0; i < (n / 2 + 1); ++i)
		{
			x0 += ds[n % 4][0];
			y0 += ds[n % 4][1];

			if (x0 < x1)
			{
				isL = true;
				continue;
			}
			if (x0 >= x2)
			{
				isR = true;
				continue;
			}

			if (y0 < y1)
			{
				isD = true;
				continue;
			}
			if (y0 >= y2)
			{
				isU = true;
				continue;
			}
			if (cb(x0, y0))
			{
				return;
			}
		}
		if (isL && isR && isU && isD)
		{
			return;
		}
	}
}


NodeMap::~NodeMap()
{
	ccc_delete_array(_loadFlags);
	ccc_delete_array(_origins);
	if (_masks)
	{
		for (int k = _map.m_MasksCount - 1; k >= 0; --k)
		{
			if (_masks[k].masks)
			{
				delete[] _masks[k].masks;
			}
		}
		delete[] _masks;
	}
}

bool NodeMap::init(const string& filename)
{
	if (!Node::init())
	{
		return false;
	}

	if (!_map.load(filename))
	{
		return false;
	}

	this->setContentSize(Size(_map.m_mw, _map.m_mh));
	_blocksCount = _map.m_bw * _map.m_bh;
	_loadFlags = new bool[_blocksCount];
	_origins = new Vec2[_blocksCount];
	for (int k = 0; k < _blocksCount; ++k)
	{
		_loadFlags[k] = false;
		_origins[k].x = (k % _map.m_bw) * _320;
		_origins[k].y = _contentSize.height - (k / _map.m_bw) * _240;
	}

	_masks = new MapReader::sMask[_map.m_MasksCount];
	for (int k = _map.m_MasksCount - 1; k >= 0; --k)
	{
		_masks[k] = _map.readMask(k, true);
		_masks[k].masks = nullptr;
	}

	_x320 = _map.m_bw / 2;
	_y240 = _map.m_bw / 2;
	_radiusx = ceil(Director::getInstance()->getWinSize().width / _320 / 2) + 1;
	_radiusy = ceil(Director::getInstance()->getWinSize().height / _240 / 2) + 1;

	scheduleUpdate();
	return true;
}



bool NodeMap::isObstacle(float x, float y)
{
	return _map.isObstacle(x / MAP_20, (_map.m_mh - 1 - y) / MAP_20);
}



bool NodeMap::loadAround(float x, float y)
{
	_x320 = x / _320;
	_y240 = (_map.m_mh - 1 - y) / _240;

	if (_x320 < 0 || _x320 >= _map.m_bw || _y240 < 0 || _y240 >= _map.m_bh)
	{
		return false;
	}
	_flag = -1;
	return true;
}


bool NodeMap::loadAround()
{
	auto func = [&](int x, int y)
	{
		if (!_loadFlags[y * _map.m_bw + x])
		{
			_flag = y * _map.m_bw + x;
			return true;
		}
		return false;
	};

	if (func(_x320, _y240))
	{
		return true;
	}

	forScrew(_x320, _y240, _map.m_bw, _map.m_bh, func, _radiusx, _radiusy);
	return true;
}



bool NodeMap::loadUnit(int flag)
{
	const auto& pter = _map.readJPG(_flag);
	Texture2D *texture = gge::Texture_Load(pter.ptr, 0, pter.size);
// 	texture->initWithImage(getImage(_flag));
// 	texture->autorelease();
	Sprite *sprite = Sprite::createWithTexture(texture);
	this->addChild(sprite, INT_MIN, _flag);
	sprite->setPosition(_origins[_flag]);
	sprite->setAnchorPoint(Vec2::ANCHOR_TOP_LEFT);


	auto lock = texture->Lock();
// cocos can create by image
	Texture2D* textureMask = gge::Texture_Create(_320, _240);
	textureMask->FillColor(0);
	auto lockMask = textureMask->Lock();

	int x = (_flag % _map.m_bw) * _320;
	int y = (_flag / _map.m_bw) * _240;
	int ix, iy;
	for (int k = _map.m_MasksCount - 1; k >= 0; --k)
	{
		const auto& m = _masks[k];
		if (m.x + m.w < x || m.x >= x + _320 || m.y + m.h < y || m.y >= y + _240)
		{
			continue;
		}
		if (!m.masks)
		{
			const auto& mask = _map.readMask(k, false);
			_masks[k].masks = new bool[m.w * m.h];
			memcpy(m.masks, mask.masks, m.w * m.h);
		}
		for (int ih = m.h - 1; ih >= 0; --ih)
		{
			for (int iw = m.w - 1; iw >= 0; --iw)
			{
				ix = m.x + iw - x;
				iy = m.y + ih - y;
				if (ix < 0 || ix >= _320 || iy < 0 || iy >= _240)
				{
					continue;
				}
				if (m.masks[ih * m.w + iw])
				{
					lockMask[iy * _320 + ix] = gge::Color_SetA(lock[iy * _320 + ix], 0x88);
				}
			}
		}
	}
	Sprite *spriteMask = Sprite::createWithTexture(textureMask);
	this->addChild(spriteMask, 0xFFFF, _flag);
	spriteMask->setPosition(sprite->getPosition());
	spriteMask->setAnchorPoint(sprite->getAnchorPoint());

	_loadFlags[_flag] = true;
	for (int k = 0; k < _blocksCount; ++k)
	{
		if (!_loadFlags[k])
		{
			return false;
		}
	}
	return true;
}




void NodeMap::update(float delta)
{
	if (_flag == -3)
	{
		++_flag;
	}
	else if (_flag == -2)
	{
		_flag = _y240 * _map.m_bw + _x320;
	}
	else if (_flag == -1)
	{
		loadAround();
	}
	else if (_flag >= 0 && _flag < 0xffff)
	{
		if (loadUnit(_flag))
		{
			_flag = 0xffff;
		}
		else
		{
			_flag = -1;
		}
	}
	else if (_flag == 0xffff)
	{
		unscheduleUpdate();
	}
}



